AWS CDK で CustomResourceConfig クラスを使って、自動作成されるカスタムリソースをカスタマイズする

AWS CDK で CustomResourceConfig クラスを使って、自動作成されるカスタムリソースをカスタマイズする

Clock Icon2024.10.24

こんにちは、製造ビジネステクノロジー部の若槻です。

最近、AWS CDK で CustomResourceConfig というクラスが提供されていることを知りました。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources.CustomResourceConfig.html

このクラスの下記のメソッドを使うことで、AWS CDK で暗黙的に自動作成されるカスタムリソースをカスタマイズすることができます。

Name Description
addLambdaRuntime Lambda 関数のランタイムバージョン
addLogRetentionLifetime Lambda 関数のログ保持期間
addRemovalPolicy Log group の削除ポリシー

CustomResourceConfig の機能は CDK Aspects の仕組みにより実現されています。CDK Aspects を使うと、スコープ内のすべての construct の検証(すべてのバケットの暗号化など)や修正(タグの追加など)を行うことができ、その仕組が内部的に使われています。

https://aws.amazon.com/jp/blogs/news/align-with-best-practices-while-creating-infrastructure-using-cdk-aspects/

注意点として CustomResourceConfig は Experimental な機能であるため、将来のバージョンで変更される可能性があります。

今回は CustomResourceConfig で利用可能な 3 つのメソッドを実際に試してみたいと思います。

試してみた

Amazon SES のReceiptRuleSet コンストラクトクラスで試してみます。この ReceiptRuleSet でdropSpam を有効化した場合に、リソース作成時に Lambda 関数が暗黙的に自動作成されます。

CustomResourceConfig を使用しない場合

まずは CustomResourceConfig を使用しない場合を確認します。ここではスコープは stack にしていますが、app にすることも可能です。

lib/sample-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ses from 'aws-cdk-lib/aws-ses';

export class SampleApp extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new ses.ReceiptRuleSet(this, 'RuleSet', {
      dropSpam: true,
    });
  }
}

CDK Diff でデプロイした場合の差分を確認すると、SES や IAM のリソースに加えて Lambda 関数が作成されることが分かります。

npx cdk diff -- SampleApp
start: Building 2be77dbc0ced986cdd58818ebb2a0115fe12b5bddd8b10717cd7dfe27a786ee7:current_account-current_region
success: Built 2be77dbc0ced986cdd58818ebb2a0115fe12b5bddd8b10717cd7dfe27a786ee7:current_account-current_region
start: Publishing 2be77dbc0ced986cdd58818ebb2a0115fe12b5bddd8b10717cd7dfe27a786ee7:current_account-current_region
success: Published 2be77dbc0ced986cdd58818ebb2a0115fe12b5bddd8b10717cd7dfe27a786ee7:current_account-current_region
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Stack SampleApp
IAM Statement Changes
┌───┬────────────────────────────────────────────────────────────────────┬────────┬───────────────────────┬──────────────────────────────┬──────────────────────────────────────────────────────────────┐
│   │ Resource                                                           │ Effect │ Action                │ Principal                    │ Condition                                                    │
├───┼────────────────────────────────────────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ + │ ${SingletonLambda224e77f9a32e4b4dac32983477abba16.Arn}             │ Allow  │ lambda:InvokeFunction │ Service:ses.amazonaws.com    │ "StringEquals": {                                            │
│   │                                                                    │        │                       │                              │   "AWS:SourceAccount": "${AWS::AccountId}"                   │
│   │                                                                    │        │                       │                              │ }                                                            │
├───┼────────────────────────────────────────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ + │ ${SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole.Arn} │ Allow  │ sts:AssumeRole        │ Service:lambda.amazonaws.com │                                                              │
└───┴────────────────────────────────────────────────────────────────────┴────────┴───────────────────────┴──────────────────────────────┴──────────────────────────────────────────────────────────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                                                       │ Managed Policy ARN                                                             │
├───┼────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

(中略)

Resources
[+] AWS::SES::ReceiptRuleSet RuleSet RuleSetE30C6C48
[+] AWS::SES::ReceiptRule RuleSet/DropSpam/Rule RuleSetDropSpamRule5809F51B
[+] AWS::IAM::Role SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole SingletonLambda224e77f9a32e4b4dac32983477abba16ServiceRole3037F5B4
[+] AWS::Lambda::Function SingletonLambda224e77f9a32e4b4dac32983477abba16 SingletonLambda224e77f9a32e4b4dac32983477abba164533EA15
[+] AWS::Lambda::Permission SingletonLambda224e77f9a32e4b4dac32983477abba16/AllowSes SingletonLambda224e77f9a32e4b4dac32983477abba16AllowSesB42DF904

✨  Number of stacks with differences: 1

マネジメントコンソールから確認したデプロイ済みのスタックのリソース一覧です。

CustomResourceConfig を使用する

次に CustomResourceConfig を使用して、先述の 3 つのメソッドにより自動作成されるリソースをカスタマイズします。

lib/sample-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ses from 'aws-cdk-lib/aws-ses';
import * as log from 'aws-cdk-lib/aws-logs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { CustomResourceConfig } from 'aws-cdk-lib/custom-resources';

export class SampleApp extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const config = CustomResourceConfig.of(this);
    config.addRemovalPolicy(cdk.RemovalPolicy.DESTROY); // 既定の Retain から変更
    config.addLogRetentionLifetime(log.RetentionDays.ONE_DAY); // 指定の保持期間のロググループをあらかじめ作成
    config.addLambdaRuntime(lambda.Runtime.NODEJS_18_X); // 既定の最新バージョンから変更

    new ses.ReceiptRuleSet(this, 'RuleSet', {
      dropSpam: true,
    });
  }
}

CDK Diff でデプロイした場合の差分を確認すると、Lambda 関数のランタイムバージョンが変更され、Log group が作成されて Lambda 関数に紐づけられていることが分かります。

$ npx cdk diff -- SampleApp
start: Building 9447d8b0d19a05509e2e82f993dedde2fffa48bb9d4794533485b015ad0130f8:current_account-current_region
success: Built 9447d8b0d19a05509e2e82f993dedde2fffa48bb9d4794533485b015ad0130f8:current_account-current_region
start: Publishing 9447d8b0d19a05509e2e82f993dedde2fffa48bb9d4794533485b015ad0130f8:current_account-current_region
success: Published 9447d8b0d19a05509e2e82f993dedde2fffa48bb9d4794533485b015ad0130f8:current_account-current_region
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Stack SampleApp
Resources
[+] AWS::Logs::LogGroup SingletonLambda224e77f9a32e4b4dac32983477abba16/Resource/logGroup SingletonLambda224e77f9a32e4b4dac32983477abba16logGroupCB6CF95E
[~] AWS::Lambda::Function SingletonLambda224e77f9a32e4b4dac32983477abba16 SingletonLambda224e77f9a32e4b4dac32983477abba164533EA15
 ├─ [+] LoggingConfig
 │   └─ {"LogGroup":{"Ref":"SingletonLambda224e77f9a32e4b4dac32983477abba16logGroupCB6CF95E"}}
 └─ [~] Runtime
     └─ @@ -1,9 +1,1 @@
        [-] {
        [-]   "Fn::FindInMap": [
        [-]     "LatestNodeRuntimeMap",
        [-]     {
        [-]       "Ref": "AWS::Region"
        [-]     },
        [-]     "value"
        [-]   ]
        [-] }
        [+] "nodejs18.x"

✨  Number of stacks with differences: 1

CDK デプロイしたスタックをマネジメントコンソールから確認すると、リソースとして Log group が作成されています。

また Lambda 関数をコンソールから確認すると、作成された Log group が紐づいています。

CloudFormation のテンプレートで作成された Log group を確認すると、RetentionInDays が 1 に設定され、DeletionPolicy が Delete に設定されています。

    "SingletonLambda224e77f9a32e4b4dac32983477abba16logGroupCB6CF95E": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "RetentionInDays": 1
      },
      "DependsOn": [
        "SingletonLambda224e77f9a32e4b4dac32983477abba16ServiceRole3037F5B4"
      ],
      "UpdateReplacePolicy": "Delete",
      "DeletionPolicy": "Delete",
      "Metadata": {
        "aws:cdk:path": "SampleApp/SingletonLambda224e77f9a32e4b4dac32983477abba16/Resource/logGroup/Resource"
      }
    },

CustomResourceConfig で行った設定がデプロイしたリソースに反映されていることが確認できました。

CustomResourceConfig が使えないパターン

次のようなパターンでは CustomResourceConfig が使えません。当初私は「removalPolicy をすべてのリソースに一括設定できるなんて便利じゃん!」なんて思っていましたが、実際は一部のリソースにしか適用できません。

  • Log group 以外のリソースの保持期間と DeletionPolicy の設定
  • LogGroup コンストラクトクラスにより明示的に作成した Log group の設定
  • Bucket コンストラクトクラスの autoDeleteObjects の設定により作成される Lambda 関数および Log group の設定

おわりに

AWS CDK で CustomResourceConfig クラスを使って、自動作成されるカスタムリソースをカスタマイズする方法についてご紹介しました。

これにより例えば、暗黙的に作成されたリソースが CDK スタックを削除した後にも削除されず残ったり、Lamabda 関数が古くて Truster Advisor で警告が出たり、という事態を回避することができます。CDK によるシステムの IaC 化をより細かいところまで行き届かせることができるので、Experimental であることに注意しつつ活用してみたいですね。

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.